#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <exception>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

#define SERVER_PORT 8554

#define SEND_FILE_LENGTH 2000000
#define RECV_FILE_LENGTH 2000000

std::string timetoStr()
{
    char tmp[64];
    time_t t = time(NULL);
    tm *_tm = localtime(&t);
    int year = _tm->tm_year + 1900;
    int month = _tm->tm_mon + 1;
    int date = _tm->tm_mday;
    int hh = _tm->tm_hour;
    int mm = _tm->tm_min;
    int ss = _tm->tm_sec;
    sprintf(tmp, "%04d%02d%02d %02d:%02d:%02d   ", year, month, date, hh, mm, ss);
    return std::string(tmp);
}
std::string GetLogNameByDate()
{
    char tmp[30];
    time_t t = time(NULL);
    tm *_tm = localtime(&t);
    int year = _tm->tm_year + 1900;
    int month = _tm->tm_mon + 1;
    int date = _tm->tm_mday;
    sprintf(tmp, "%04d%02d%02d.txt", year, month, date);
    return std::string(tmp);
}
void ErrorHandling(const std::string &message)
{
    std::cerr << message << std::endl;
    std::exit(1);
}

// 客户端上传文件处理函数
void ClientUploadFile(int s_accept, const std::string &file_name, int file_size,
                      const std::string &client_IP, std::ostream &out_stream, uint8_t *wait_recv)
{
    int send_len = 0, recv_len = 0;
    char send_buf[SEND_FILE_LENGTH], recv_buf[RECV_FILE_LENGTH];
    std::memset(send_buf, '\0', sizeof(send_buf));
    std::memset(recv_buf, '\0', sizeof(recv_buf));
    std::string send_info;

    std::ofstream outfile;
    outfile.open(file_name.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
    if (!outfile.good())
    {
        // 文件无法打开
        send_len = send(s_accept, "error", SEND_FILE_LENGTH, 0);
        out_stream << "客户端为：" << client_IP << " 发送给客户端的信息："
                   << "error" << std::endl;
        if (send_len < 0)
        {
            out_stream << "客户端为：" << client_IP << " 发送客户端信息失败！断开连接" << std::endl;
            close(s_accept);
            return;
        }
        out_stream << "客户端为：" << client_IP << " 上传的文件无法建立而断开连接" << std::endl;
        close(s_accept);
        return;
    }
    // 文件打开成功，开辟缓冲区，并返回客户端结果
    else
    {
        char *data = NULL;
        try
        {
            data = new char[file_size];
        }
        catch (const std::exception &)
        {
            // 失败，发送error
            std::memset(send_buf, '\0', sizeof(send_buf));
            strcpy(send_buf, "error");
            send_len = send(s_accept, send_buf, SEND_FILE_LENGTH, 0);
            out_stream << "客户端为：" << client_IP << " 发送给客户端的信息：" << send_buf
                       << std::endl;
            if (send_len < 0)
            {
                out_stream << "客户端为：" << client_IP << " 发送客户端信息失败！断开连接"
                           << std::endl;
                close(s_accept);
                return;
            }
            out_stream << "客户端为：" << client_IP << " 服务端开辟缓冲区失败！断开连接"
                       << std::endl;
            close(s_accept);
            return;
        }
        // 成功，发送ok
        std::memset(send_buf, '\0', sizeof(send_buf));
        strcpy(send_buf, "ok");
        send_len = send(s_accept, send_buf, SEND_FILE_LENGTH, 0);
        out_stream << "客户端为：" << client_IP << " 发送给客户端的信息：" << send_buf << std::endl;
        if (send_len < 0)
        {
            out_stream << "客户端为：" << client_IP << " 发送客户端信息失败！断开连接" << std::endl;
            close(s_accept);
            return;
        }
        recv_len = recv(s_accept, data, file_size, 0);
        outfile.write(data, file_size);
        outfile.close();
        if (recv_len < file_size)
        {
            // 接收的信息不全，需要删除文件
            remove(file_name.c_str());
            out_stream << "客户端为：" << client_IP << " 接收客户端文件信息失败！断开连接"
                       << std::endl;
            close(s_accept);
            return;
        }
        else
            out_stream << "客户端为：" << client_IP << " 接收客户端文件成功！断开连接" << std::endl;

        delete[] data;

        close(s_accept);
        *wait_recv = 0;
    }
}

int main(int argc, char *argv[])
{
    std::string log_file_name = GetLogNameByDate();
    std::ofstream Log;
    try
    {
        Log.open(log_file_name.c_str(), std::ofstream::out | std::ofstream::app);
        std::cout << "创建日志文件成功！" << std::endl;
    }
    catch (std::ios_base::failure &e)
    {
        std::cout << "创建日志文件失败！退出程序" << std::endl;
        std::exit(1);
    }

    Log << timetoStr() << "启动:文件传输TCP实现——服务端。。。" << std::endl;

    int send_len = 0, recv_len = 0;
    int len = 0;
    char send_buf[SEND_FILE_LENGTH], recv_buf[RECV_FILE_LENGTH];
    std::string na;
    std::memset(send_buf, '\0', sizeof(send_buf));
    std::memset(recv_buf, '\0', sizeof(recv_buf));
    std::string send_info;

    int s_server, s_accept;
    std::string client_IP;
    sockaddr_in server_addr, clnt_addr;
    s_server = socket(AF_INET, SOCK_STREAM, 0);
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);

    if (bind(s_server, (sockaddr *)&server_addr, sizeof(sockaddr)) == -1)
        ErrorHandling("套接字绑定失败！");

    if (listen(s_server, SOMAXCONN) == -1) ErrorHandling("监听套接字失败！");

    uint8_t wait_recv = 1;

    while (wait_recv)
    {
        len = sizeof(sockaddr);
        s_accept = accept(s_server, (sockaddr *)&clnt_addr, (socklen_t *)&len);
        if (s_accept == -1) ErrorHandling("连接失败！");
        printf("connect\n");
        client_IP = inet_ntoa(clnt_addr.sin_addr);
        Log << timetoStr() << "连接" << client_IP << "成功！" << std::endl;

        std::memset(recv_buf, '\0', 100);
        recv_len = recv(s_accept, recv_buf, RECV_FILE_LENGTH, 0);
        if (recv_len < 0)
        {
            Log << timetoStr() << "客户端为：" << client_IP << " 接收数据失败！断开连接"
                << std::endl;
            close(s_accept);
            continue;
        }
        else
        {
            Log << timetoStr() << "客户端为：" << client_IP << " 接收来自客户端的信息：" << recv_buf
                << std::endl;
            FILE *file = fopen("recv_file.txt", "w+");

            if (file == nullptr)
            {
                perror("Error opening file");
                return EXIT_FAILURE;
            }
            // 写入一行文本
            fprintf(file, "%s", recv_buf);

            // 关闭文件
            if (fclose(file) != 0)
            {
                perror("Error closing file");
                return EXIT_FAILURE;
            }
        }

        std::string order, file_name, Size;
        size_t id_addr1, id_addr2;
        int size_of_file;

        std::string file_info(recv_buf);
        id_addr1 = file_info.find_first_of(':');
        order = file_info.substr(0, id_addr1);

        id_addr2 = file_info.find_first_of(':', id_addr1 + 1);
        file_name = file_info.substr(id_addr1 + 1, id_addr2 - id_addr1 - 1);
        Size = file_info.substr(id_addr2 + 1, file_info.length() - id_addr2 - 1);

        if (order == "upload")
        {
            ClientUploadFile(s_accept, file_name, atoi(Size.c_str()), client_IP, Log, &wait_recv);
        }
    }

    Log.close();
    close(s_server);
    return 0;
}